#!/usr/bin/env python
# -*- coding:utf-8 -*-

'''
# Author:linjinting for 02010166
@file: tcp_client.py
@time: 2020/3/14 15:15
'''

import socket
import selectors
from unit import find_caller


class TCPClient(object):
    __TYPE__ = 'tcp'

    def __init__(self, *args, **kwargs):
        self.m_local_address = None
        self.m_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)

        if args:
            if not isinstance(args[0], tuple):
                raise TypeError("args must be tuple")
            self.m_remote_address = args[0]
        if not self.m_remote_address:
            return
        self.callback = kwargs.get('callback')
        self.m_channel = kwargs.get('channel')

        timeout = kwargs.get('timeout', 10)
        # blocking = kwargs.get('blocking', False)
        self.m_local_address = kwargs.get('laddr', None)

        self._max_listen = kwargs.get('max_listen', 100)
        if timeout:
            self.m_socket.settimeout(timeout)  # 设置 timeout
        # self.m_socket.setblocking(blocking)  # 设置 blocking ，默认非阻塞

        self.m_selector = selectors.DefaultSelector()  # 生成select实例对象

    def start(self):
        try:
            if self.m_local_address:
                self.m_socket.bind(self.m_local_address)
                self.print_and_logger(msg='[client] bind local address success\n', type='logger', level='info',
                                      channel=self.m_channel, caller=find_caller(),
                                      laddr=self.m_local_address, raddr=self.m_remote_address)

            self.m_socket.connect(self.m_remote_address)
            if self.m_local_address is None:
                self.m_local_address = self.m_socket.getsockname()
            self.print_and_logger(msg='[client] connect  success\n', name='connect', type='event', level='info',
                                  socketType='tcpClient', channel=self.m_channel, caller=find_caller(),
                                  laddr=self.m_local_address, raddr=self.m_remote_address)

            self.m_socket.setblocking(False)
            self.m_selector.register(self.m_socket, selectors.EVENT_READ, self.read)
            self.monitor()
        except OSError as e:
            # if ret.errno == 10035:
            self.print_and_logger(msg=e.__str__(), name='close', type='event', level='error',
                                  socketType='tcpClient', channel=self.m_channel, caller=find_caller(),
                                  laddr=self.m_local_address, raddr=self.m_remote_address)

    def monitor(self):
        """监听EVENT事件列表,有活动的事件就交给相应方法处理"""

        while True:
            try:
                # 调用select：优先使用epoll
                events = self.m_selector.select()
                # 只要不阻塞就有调用的数据，返回一个列表
                # 默认阻塞，有活动链接就返回活动的链接列表
                for key, mask in events:
                    # callback相当于调accept函数
                    callback = key.data
                    # 获取函数内存地址，加入参数
                    # key.fileobj = 文件句柄
                    callback(key.fileobj)
            except OSError:
                break

    def read(self, conn):
        try:
            data = conn.recv(1024)  # Should be ready
        except WindowsError:
            pass
        else:
            if data:
                self.print_and_logger(msg='[client] recv msg\n', data=data, name='read', type='event', level='info',
                                      side='recv', channel=self.m_channel, client=self,
                                      caller=find_caller(), laddr=self.m_local_address, raddr=self.m_remote_address)

            else:
                self.print_and_logger(msg='[client] remote server positive closing\n', name='close', type='event', level='info',
                                      side='recv', channel=self.m_channel, caller=find_caller(),
                                      laddr=self.m_local_address, raddr=self.m_remote_address)
                # 取消注册
                self.m_selector.unregister(conn)
                # 关闭链接
                conn.close()

    def send_msg(self, data, isReply=False):
        self.m_socket.send(data)
        if isReply:
            msg = '[client][reply] send msg success\n'
        else:
            msg = '[client] send msg success\n'
        self.print_and_logger(msg=msg, data=data, type='logger', level='info',
                              side='send', channel=self.m_channel,
                              caller=find_caller(), laddr=self.m_local_address, raddr=self.m_remote_address)
        # return

    def close(self):
        try:
            self.m_socket.close()
            self.print_and_logger(msg='[client] closed\n', type='logger', level='info',
                                  channel=self.m_channel, caller=find_caller(),
                                  laddr=self.m_local_address, raddr=self.m_remote_address)
        except OSError as e:
            self.print_and_logger(msg=e.__str__(), type='logger', level='error',
                                  channel=self.m_channel, caller=find_caller(),
                                  laddr=self.m_local_address, raddr=self.m_remote_address)

    def set_channel(self, channel):
        self.m_channel = channel

    def print_and_logger(self, **kwargs):
        if self.callback:
            # print(kwargs)
            self.callback(**kwargs)
        else:
            print(kwargs)


if __name__ == '__main__':
    client = TCPClient(('127.0.0.1', 5566), laddr=('192.168.1.2', 1234))
    print(type(client))
    # client.start()
